package indi.flynn.zhouyi.log;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


import indi.flynn.zhouyi.log.annotations.BizFlowLog;
import indi.flynn.zhouyi.log.enums.BizFlowEnum;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import indi.flynn.zhouyi.log.BizFlowLogger.BizFlowId;

/**
 * 业务流程日志AOP处理器
 * 用于拦截被@BizFlowLog注解标记的方法，自动处理业务流程日志
 */
@Aspect
@Component
public class BizFlowLogAspect {

    private static final Logger log = LoggerFactory.getLogger(BizFlowLogAspect.class);
    // SpEL表达式解析器
    private final ExpressionParser expressionParser = new SpelExpressionParser();

    // 参数名发现器，用于获取方法参数名
    private final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();

    // 方法参数名缓存，key为方法对象，value为参数名数组
    private final Map<Method, String[]> parameterNamesCache = new ConcurrentHashMap<>();

    // SpEL表达式缓存，key为表达式字符串，value为解析后的表达式对象
    private final Map<String, Expression> expressionCache = new ConcurrentHashMap<>();

    // bizIdParams解析结果缓存，key为方法对象，value为解析后的参数列表
    private final Map<Method, List<BizIdParam>> bizIdParamsCache = new ConcurrentHashMap<>();

    /**
     * 表示解析后的bizIdParam
     */
    private static class BizIdParam {
        private final String paramName;
        private final String expressionString;
        private final Expression expression;

        public BizIdParam(String paramName, String expressionString, Expression expression) {
            this.paramName = paramName;
            this.expressionString = expressionString;
            this.expression = expression;
        }
    }

    /**
     * 环绕通知，处理被@BizFlowLog注解标记的方法
     *
     * @param joinPoint 连接点
     * @return 方法执行结果
     * @throws Throwable 方法执行异常
     */
    @Around("@annotation(indi.flynn.zhouyi.log.annotations.BizFlowLog)")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        // 获取方法签名
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        // 获取注解
        BizFlowLog bizFlowLog = method.getAnnotation(BizFlowLog.class);
        if (bizFlowLog == null) {
            // 如果没有注解，直接执行原方法
            return joinPoint.proceed();
        }

        // 获取业务流程
        BizFlowEnum bizFlow = bizFlowLog.value();
        if (bizFlow == null) {
            // 如果没有业务流程，直接执行原方法
            return joinPoint.proceed();
        }

        // 获取方法参数
        Object[] args = joinPoint.getArgs();

        // 构建业务流程ID
        BizFlowId bizFlowId = null;

        try {
            bizFlowId = buildBizFlowId(bizFlowLog.bizIdParams(), args, method);
        } catch (Throwable e) {
            log.error("BizFlowLogAspect.buildBizFlowId error", e);
        }

        // 初始化上下文
        BizFlowLogger.initContext(bizFlow, bizFlowId);
        try {
            // 执行原方法
            return joinPoint.proceed();
        } finally {
            // 清理化上下文
            BizFlowLogger.clearContext();
        }
    }

    /**
     * 根据bizIdParams构建BizFlowId
     * 使用SpEL表达式从方法参数中提取值
     *
     * @param bizIdParams 业务流程ID参数列表
     * @param args 方法参数
     * @param method 方法对象，用于获取参数名
     * @return 业务流程ID
     */
    private BizFlowId buildBizFlowId(String[] bizIdParams, Object[] args, Method method) {
        BizFlowId bizFlowId = new BizFlowId();

        if (bizIdParams == null || bizIdParams.length == 0) {
            return bizFlowId;
        }

        // 创建SpEL表达式上下文
        StandardEvaluationContext context = new StandardEvaluationContext();

        // 将方法参数放入上下文（使用索引方式，兼容旧的写法）
        context.setVariable("args", args);

        // 从缓存获取方法参数名，如果缓存中没有则获取并缓存
        String[] parameterNames = parameterNamesCache.computeIfAbsent(method,
                parameterNameDiscoverer::getParameterNames);

        if (parameterNames != null) {
            // 将参数名和参数值放入上下文
            for (int i = 0; i < parameterNames.length && i < args.length; i++) {
                context.setVariable(parameterNames[i], args[i]);
            }
        }

        // 从缓存获取解析后的bizIdParams，如果缓存中没有则解析并缓存
        List<BizIdParam> parsedParams = bizIdParamsCache.computeIfAbsent(method, m -> {
            List<BizIdParam> params = new ArrayList<>();
            for (String param : bizIdParams) {
                // 解析参数格式：参数名=表达式
                int equalsIndex = param.indexOf('=');
                if (equalsIndex <= 0 || equalsIndex == param.length() - 1) {
                    continue;
                }

                String paramName = param.substring(0, equalsIndex).trim();
                String expressionString = param.substring(equalsIndex + 1).trim();

                try {
                    // 从缓存获取表达式，如果缓存中没有则解析并缓存
                    Expression expression = expressionCache.computeIfAbsent(expressionString,
                            expressionParser::parseExpression);

                    params.add(new BizIdParam(paramName, expressionString, expression));
                } catch (Exception e) {
                    // 表达式解析出错，记录日志
                    System.err.println("解析SpEL表达式出错: " + expressionString + ", 错误: " + e.getMessage());
                }
            }
            return params;
        });

        // 执行缓存的表达式
        for (BizIdParam param : parsedParams) {
            try {
                // 执行SpEL表达式
                Object value = param.expression.getValue(context);

                // 添加参数到业务流程ID
                if (value != null) {
                    bizFlowId.addParam(param.paramName, value.toString());
                }
            } catch (Exception e) {
                // 表达式解析或执行出错，记录日志
                log.error("BizFlowLogAspect.buildBizFlowId error, param:{}", param, e);
            }
        }

        return bizFlowId;
    }
}

